package com.free.bsf.eureka.client.customip;

import com.free.bsf.core.util.*;
import com.free.bsf.eureka.client.EurekaMonitor;
import com.free.bsf.eureka.client.EurekaProperties;
import com.netflix.client.ClientRequest;
import feign.Request;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import lombok.val;
import lombok.var;
import org.apache.commons.lang.StringUtils;

import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author: chejiangyi
 * @version: 2020-01-14 15:09
 * com.free.bsf.eureka.client.customip.CustomIpExtend.
 **/
public class CustomIpExtend {
    private static String newMethodCode() {
        //FeignLoadBalancer.RibbonRequest.toRequest($0,$$);
        String code = "{\n" +
                "feign.Request o= com.free.bsf.eureka.client.customip.CustomIpExtend.toRequest($0,$1);\n" +
                "if(o==null){return $0.toRequestOld($$);}" +
                "return o;" +
                "}";
        return code;
    }
    private volatile static boolean isload = false;

    public static void setCustomIpHook() {
        if(!PropertyUtils.getPropertyCache("bsf.ribbon.customIp.enabled",false)){
            return;
        }
        try {
            ClassPool classPool = ClassPoolUtils.getInstance();
            CtClass ctClass = classPool.get("org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer$RibbonRequest");
            if (!isload) {
                isload = true;
                CtMethod ctMethod = ctClass.getDeclaredMethod("toRequest",new CtClass[]{classPool.get("feign.Request")});
                CtMethod mold = CtNewMethod.copy(ctMethod, "toRequestOld", ctClass, null);
                ctClass.addMethod(mold);
                ctMethod.setBody(newMethodCode());

                if (ctClass.isFrozen()) {
                    ctClass.defrost();
                }
                ctClass.toClass();
                LogUtils.info(EurekaMonitor.class, EurekaProperties.Project, "注入FeignLoadBalancer.RibbonRequest ok");
            }
        } catch (Exception exp) {
            LogUtils.error(EurekaMonitor.class,  EurekaProperties.Project, "注入FeignLoadBalancer.RibbonRequest 异常", exp);
        }
    }

    public static Request toRequest(ClientRequest obj, Request request){
        try {
            boolean hasBsfUrl = request.headers().containsKey("bsf-url");
            if(hasBsfUrl||!StringUtils.isEmpty(request.url())) {
                val host = new URI(request.url()).getHost();
                if(hasBsfUrl||host!=null) {
                    var uri = obj.getUri();
                    Map<String, Collection<String>> headers = new LinkedHashMap(request.headers());
                    if(hasBsfUrl){
                        uri=new URI((String)request.headers().get("bsf-url").toArray()[0]);
                    }else {
                        val hosts = PropertyUtils.getPropertyCache("ribbon.customIp." + host + ".host", "");
                        if (!StringUtils.isEmpty(hosts)) {
                            uri = new URI(StringUtils.stripEnd(hosts, "/") + "/" + StringUtils.stripStart(uri.getPath(), "/"));
                            headers.put("bsf-url", Arrays.asList(uri.toASCIIString()));
                        }
                    }
                    return Request.create(request.httpMethod(), uri.toASCIIString(), headers, request.requestBody());
                }
            }

        }catch (Exception e){
            LogUtils.error(CustomIpExtend.class,EurekaProperties.Project,"自定义feign url出错",e);
        }
        //原始代码
        return null;
    }
}
